<?php

namespace common\db;

use Yii;
use yii\web\NotFoundHttpException;
use yii\behaviors\TimestampBehavior;
use yii\db\Query;
use yii\db\Expression;
use yii\helpers\Inflector;
use common\base\ModelHttpException;

/**
 * ActiveRecord is the base class for classes representing relational data in terms of objects.
 * 
 * @author emhome<emhome@163.com>
 * @since 2.0
 */
class ActiveRecord extends \yii\db\ActiveRecord {

    use \common\traits\ModelErrors;
    use \common\traits\DataPierces;
    use \common\traits\SearchForBuildQuery;

    /**
     * @inheritdoc
     */
    public $timestampAttributes = [
        'createdAtAttribute' => 'created_at',
        'updatedAtAttribute' => 'updated_at',
    ];

    /**
     * @inheritdoc
     */
    public $activeAttributes = [
        'deletedAtAttribute' => 'deleted_at',
        'activeStatusAttribute' => 'status',
    ];

    /**
     * @inheritdoc
     */
    public function behaviors() {
        if (empty($this->timestampAttributes)) {
            return parent::behaviors();
        }
        $timestampBehavior = [
            'class' => TimestampBehavior::className(),
        ];
        foreach ($this->timestampAttributes as $key => $vls) {
            $timestampBehavior[$key] = $this->hasAttribute($vls) ? $vls : false;
        }
        return [
            $timestampBehavior
        ];
    }

    /**
     * @return string the fullschema tablename.
     */
    public static function getSchemaTableName($tablename) {
        $db = static::getDb();
        preg_match("/dbname=([^;]+)/i", $db->dsn, $matches);
        return $db->getSchema()->getRawTableName($matches[1] . '.' . $tablename);
    }

    /**
     * {@inheritdoc}
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
     */
    public static function find() {
        return Yii::createObject(ActiveQuery::className(), [get_called_class()]);
    }

    /**
     * {@inheritdoc}
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
     */
    public static function callProcedure($name, $params = [], $db = null) {
        if (!$db) {
            $db = static::getDb();
        }
        $sql = "CALL $name";
        $tempKeys = [];
        $inParams = [];
        $outFileds = [];
        if (!empty($params)) {
            foreach ($params as $key => $vls) {
                if (is_int($key)) {
                    $outkey = '@' . ltrim($vls, '@');
                    $tempKeys[] = $outkey;
                    $outFileds[] = $outkey;
                } elseif (strpos($key, '@') === 0) {
                    $tempKeys[] = $key;
                    $outFileds[] = $key;
                } elseif (strpos($key, ':') === 0) {
                    $tempKeys[] = $key;
                    $inParams[$key] = $vls;
                } else {
                    $tempKeys[] = ':' . $key;
                    $inParams[':' . $key] = $vls;
                }
            }
        }
        if (!empty($tempKeys)) {
            $sql .= '(' . implode(',', $tempKeys) . ')';
        }
        $sql .= ';';
        $query = $db->createCommand($sql)->bindValues($inParams);
        if (empty($outFileds)) {
            return $query;
        }
        $query->execute();
        return $db->createCommand('SELECT ' . implode(',', $outFileds) . ';');
    }

    /**
     * @param integer $unionId 是否替换数据,如果为真, 则创建REPLACE INTO sql语句 (Only Mysql)
     * @param \yii\db\Connection $db
     * @see \yii\db\Command::batchInsert();
     */
    public static function callFunction($name, $params = [], $db = null) {
        if (!$db) {
            $db = static::getDb();
        }
        $bindParams = [];
        if (!empty($params)) {
            $params = (array) $params;
            foreach ($params as $key => $vls) {
                $_prefix = ':';
                if (is_numeric($key)) {
                    $_prefix .= 'p';
                }
                $bindParams[$_prefix . $key] = $vls;
            }
        }
        $function = $name . '(' . implode(',', array_keys($bindParams)) . ')';
        $expression = new Expression($function, $bindParams);
        return (new Query())->select($expression)->scalar($db);
    }

    /**
     * 加入记录软删除
     * @param boolean $hard 是否真实删除
     * {@inheritdoc}
     */
    public function delete($hard = false) {
        if ($hard == false && $this->hasAttribute('deleted_at')) {
            $this->deleted_at = time();
            return $this->save();
        }
        return parent::delete();
    }

    /**
     * 查询对象是否存在存在返回记录不存在创建
     * @params $condition
     * @return static
     */
    public static function findOrNew($condition) {
        $model = static::findOne($condition);
        if ($model === null) {
            $className = self::className();
            $model = new $className();
            if (is_array($condition)) {
                $model->setAttributes($condition);
            }
        }
        return $model;
    }

    /**
     * 查询对象是否存在存在返回记录不存在创建
     * @params $condition
     * @return static
     */
    public static function findOrCreate($condition) {
        $model = static::findOne($condition);
        if ($model === null) {
            $className = self::className();
            $model = new $className();
            if (is_array($condition)) {
                $model->setAttributes($condition);
            }
            if (!$model->save()) {
                throw new ModelHttpException('数据更新异常', $model->getErrorMsg());
            }
        }
        return $model;
    }

    /**
     * 查询对象是否存在存在返回记录不存在返回异常
     * @params $condition
     * @return static
     */
    public static function findOrFail($condition) {
        $model = static::findOne($condition);
        if ($model === null) {
            throw new NotFoundHttpException('未找到数据模型对应的记录！');
        }
        return $model;
    }

    /**
     * {@inheritdoc}
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
     */
    public static function findActive($alias = null, $state = false) {
        $query = static::find();
        if ($alias) {
            $query->alias($alias);
        }
        $query->active($state);
        return $query;
    }

    /**
     * {@inheritdoc}
     * @return static|null ActiveRecord instance matching the condition, or `null` if nothing matches.
     */
    public static function findActiveOne($condtion) {
        $query = static::find()->active();
        $query->andWhere($condtion);
        $model = $query->one();
        if ($model === null) {
            throw new NotFoundHttpException('未找到数据模型对应的记录！');
        }
        return $model;
    }

    public function hasOneX($class, $link, $pk) {
        /* @var $class SequenceActiveRecord */
        /* @var $query ActiveQuery */
        static::log($pk);
        $query = $class::findx($pk);
        $query->primaryModel = $this;
        $query->link = $link;
        $query->multiple = false;
        return $query;
    }

    public function hasManyX($class, $link, $pk) {
        /* @var $class SequenceActiveRecord */
        /* @var $query ActiveQuery */
        $query = $class::findx($pk);
        $query->primaryModel = $this;
        $query->link = $link;
        $query->multiple = true;
        return $query;
    }

    /**
     * Sets the named attribute value.
     * @param string $name the attribute name
     * @param mixed $value the attribute value.
     * @throws InvalidArgumentException if the named attribute does not exist.
     * @see hasAttribute()
     */
    public function setGeometryAttribute($name, $value) {
        if (is_array($value) && count($value) == 2) {
            list($lng, $lat) = $value;
            //$value = new Expression("GEOMFROMTEXT('POINT($lng $lat)')");
            $value = new Expression("POINT($lng,$lat)");
        }
        $this->setAttribute($name, $value);
    }

    /**
     * Populates the model with input data.
     */
    public function getCommonCache() {
        if (Yii::$app->has('commonCache')) {
            return Yii::$app->commonCache;
        }
        return Yii::$app->cache;
    }

    /**
     * 记录AR日志
     */
    public static function log($content = null) {
        if (!YII_DEBUG) {
            return;
        }
        if ($content instanceof ActiveQuery) {
            $content->debug();
        } else {
            Yii::error($content, __METHOD__);
        }
    }

    /**
     * Returns attribute values.
     * @param array $names list of attributes whose value needs to be returned.
     * Defaults to null, meaning all attributes listed in [[attributes()]] will be returned.
     * If it is an array, only the attributes in the array will be returned.
     * @param array $except list of attributes whose value should NOT be returned.
     * @return array attribute values (name => value).
     */
    public function getCamelAttributes($names = null, $except = array()) {
        $values = [];
        if ($names === null) {
            $names = $this->attributes();
        }
        foreach ($names as $name) {
            $key = Inflector::variablize($name, '_');
            $values[$key] = $this->$name;
        }
        foreach ($except as $name) {
            $key = Inflector::variablize($name, '_');
            unset($values[$key]);
        }
        return $values;
    }

    protected function buildCachePrefix($attribute) {
        $primaryKey = $this->primaryKey;
        $cacheName = array_filter([
            static::class,
            $attribute,
            $primaryKey
        ]);
        return implode('_', $cacheName);
    }

    public function getCacheAttributes($attribute = null, $duration = 1800) {
        $cache = Yii::$app->cache;
        $cacheName = $this->buildCachePrefix($attribute);
        $data = $cache->get($cacheName);
        if ($data !== false) {
            return $data;
        }
        if ($attribute === null) {
            $data = $this->attributes;
        } else {
            $method = 'get' . ucfirst($attribute);
            $handler = array($this, $method);
            if ($this->hasMethod($method) && is_callable($handler)) {
                $data = call_user_func_array($handler, [$this]);
            } elseif ($this->hasAttribute($attribute)) {
                $data = $this->getAttribute($attribute);
            }
        }
        if ($data !== false) {
            $cache->set($cacheName, $data, $duration);
        }
        return $data;
    }

    public function getIdentifyUser() {
        if (!Yii::$app->user->isGuest) {
            return Yii::$app->user->identity;
        }
        return null;
    }

    public function isActived($state = null) {
        if ($this->hasAttribute('deleted_at')) {
            $deleted = boolval($this->getAttribute('deleted_at'));
            if ($deleted) {
                return !$deleted;
            }
        }
        if ($state !== null && $this->hasAttribute('status')) {
            $status = $this->getAttribute('status');
            if ($status != $state) {
                return false;
            }
        }
        return true;
    }

}
